package com.totem.order.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.totem.admin.mapper.TAppConfigItemMapper;
import com.totem.admin.mapper.TAppConfigMapper;
import com.totem.admin.mapper.TFundDailyEndMapper;
import com.totem.admin.mapper.TSubscribeDailyEndMapper;
import com.totem.admin.vo.FundBalanceVO;
import com.totem.base.constants.CommonConstants;
import com.totem.base.constants.ErrorCode;
import com.totem.base.constants.SecurityComponets;
import com.totem.base.constants.UrlConstants;
import com.totem.base.enums.*;
import com.totem.base.exception.BaseException;
import com.totem.base.model.Result;
import com.totem.base.mybatis.UserIdContext;
import com.totem.base.util.DateUtils;
import com.totem.customer.mapper.TCustomerMapper;
import com.totem.customer.model.TCustomers;
import com.totem.order.mapper.*;
import com.totem.order.model.*;
import com.totem.order.service.IOrderCommonService;
import com.totem.order.service.ITOrderService;
import com.totem.order.vo.*;
import com.totem.product.mapper.TProductInvMapper;
import com.totem.product.mapper.TProductMapper;
import com.totem.product.mapper.TProductSpecMapper;
import com.totem.product.mapper.TSubscribeMapper;
import com.totem.product.model.TProductInv;
import com.totem.product.model.TProductSpec;
import com.totem.product.model.TProducts;
import com.totem.product.model.TSubscribe;
import com.totem.product.service.ProductCommonService;
import com.totem.product.vo.ProductDetailVO;
import com.totem.product.vo.ProductSpecVO;
import com.totem.product.vo.ProductVO;
import com.totem.startup.InitialGlobalConfig;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.argon2.Argon2PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.client.RestTemplate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TOrderServiceImpl extends ServiceImpl<TOrderMapper, TOrders> implements ITOrderService {
    private final Argon2PasswordEncoder argon2Encoder = SecurityComponets.ARGON2_PASSWORD_ENCODER;

    @Autowired
    private TCustomerMapper tCustomerMapper;
    @Autowired
    private TProductMapper tProductMapper;
    @Autowired
    private ProductCommonService productCommonService;
    @Autowired
    private TProductInvMapper tProductInvMapper;
    @Autowired
    private TProductSpecMapper tProductSpecMapper;
    @Autowired
    private TWalletMapper tWalletMapper;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private TWalletJournalMapper tWalletJournalMapper;
    @Autowired
    private TCollectionJournalMapper tCollectionJournalMapper;
    @Autowired
    private TFundOrderItemMapper tFundOrderItemMapper;
    @Autowired
    private TFundDailyEndMapper tFundDailyEndMapper;
    @Autowired
    private TGoodsOrderItemMapper tGoodsOrderItemMapper;
    @Autowired
    private IOrderCommonService iOrderCommonService;
    @Autowired
    private TRechargeOrderItemMapper tRechargeOrderItemMapper;
    @Autowired
    private TSubscribeOrderItemMapper tSubscribeOrderItemMapper;
    @Autowired
    private TSubscribeDailyEndMapper tSubscribeDailyEndMapper;
    @Autowired
    private TWithdrawOrderItemMapper tWithdrawOrderItemMapper;
    @Autowired
    private TOrderMapper tOrderMapper;
    @Autowired
    private TAppConfigMapper tAppConfigMapper;
    @Autowired
    private TAppConfigItemMapper tAppConfigItemMapper;
    @Autowired
    private TSubscribeMapper tSubscribeMapper;

    private final InitialGlobalConfig globalConfig = InitialGlobalConfig.instance();

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public GoodsOrderVO orderGoods(GoodsOrderVO vo) {
        Date now = new Date();
        vo.setOrderDate(now);
        String currency = vo.getCurrency();
        CurrencyEnum currencyEnum = CurrencyEnum.byCode(currency);
        if(currencyEnum==null){
            log.warn("支付方式错误, 不支持[{}]", currency);
            throw new BaseException(ErrorCode.ILLEGAL_CURRENCY);
        }
        Long customerId = UserIdContext.getUserId();
        if(customerId==null){
            log.warn("当前客户不存在");
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }
        TCustomers customer = tCustomerMapper.selectById(customerId);
        if(customer==null){
            log.warn("Id为[{}]的客户不存在", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER);
        }
        if(StateEnum.isStop(customer.getState())){
            log.warn("Id为[{}]的客户已被封号", customerId);
            throw new BaseException(ErrorCode.STOPPED_CUSTOMER);
        }
        Long productId = vo.getProductId();
        TProducts tProducts = tProductMapper.selectById(productId);
        if(tProducts==null){
            log.warn("商品[{}]不存在", vo.getProductId());
            throw new BaseException(ErrorCode.NOT_EXIST_PRODUCT);
        }
        List<ProductVO> productVOList = productCommonService.list(List.of(productId));

        ProductVO productVO = productVOList.get(0);
        vo.setProductName(productVO.getProductName());
        vo.setProductSpecVO(productVO.getLatestProductSpecVO());
        vo.setCreateByNickname(productVO.getCreateByNickname());

        ProductDetailVO detailVO = productVO.getProductDetailVO();
        String restrictType = detailVO.getRestrictType();
        Integer restrictMin = detailVO.getRestrictMin();
        Integer restrictMax = detailVO.getRestrictMax();
        if(restrictMax==null){
            restrictMax=0;
        }
        if(RestrictTypeEnum.UNRESTRICT.getCode().equalsIgnoreCase(restrictType)){
        }
        // 单次限购,每次限购多少
        if(RestrictTypeEnum.SINGLE.getCode().equalsIgnoreCase(restrictType)){
        }
        // 长期限购,总共限购多少
        if(RestrictTypeEnum.LONG_TERM.getCode().equalsIgnoreCase(restrictType)){
            // 已购买数
            int buyNum = tGoodsOrderItemMapper.countByCustomerId(customerId);
            if(buyNum>=restrictMax){
                log.warn("客户[{}]已达到长期限购限制,无法购买商品[{}]", customerId, productId);
                throw new BaseException(ErrorCode.PURCHASE_LIMIT);
            }
        }

        BigDecimal txAmt = vo.getAmount();
        Integer txNum = 1;
        ProductSpecVO latestSpec = productVO.getLatestProductSpecVO();
        // currency = productVO.getProductSpecVO().getCurrency();
        Long specId = latestSpec.getId();
        Integer specUsable = latestSpec.getUsable();
        BigDecimal price = latestSpec.getPrice();
        if(price.compareTo(txAmt)!=0){
            log.warn("客户[{}]的订单金额[{}]错误, 与产品价格[{}]不符", customerId, txAmt, price);
            throw new BaseException(ErrorCode.ORDER_PRICE_CONFLICT);
        }

        TWallets myWallet = tWalletMapper.selectById(customerId);
        if(myWallet==null){
            log.warn("客户[{}]钱包未找到", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_WALLET);
        }

        TOrders tOrders = TOrders.builder()
                .customerId(customerId)
                .orderType(OrderTypeEnum.GOODS.getCode())
                .amount(vo.getAmount())
                .orderTime(now)
                .paymentState(PaymentStateEnum.UNPAID.getCode())
                .state(StateEnum.NORMAL.getCode())
                .build();
        tOrderMapper.insert(tOrders);

        TGoodsOrderItems tGoodsOrderItems = TGoodsOrderItems.builder()
                .orderId(tOrders.getId())
                .productId(productId)
                .currency(currency)
                .num(txNum)
                .amount(price)
                .build();
        tGoodsOrderItemMapper.insert(tGoodsOrderItems);

        TProductSpec tProductSpec = TProductSpec.builder()
                .id(specId)
                .usable(specUsable-txNum)
                .build();
        tProductSpecMapper.updateById(tProductSpec);

        LambdaQueryWrapper<TProductInv> invQuery = new LambdaQueryWrapper<>();
        invQuery.eq(TProductInv::getProductId, productId);
        TProductInv tProductInv = tProductInvMapper.selectOne(invQuery);

        TProductInv updateInv = TProductInv.builder()
                .id(tProductInv.getId())
                .freeze(tProductInv.getFreeze()+txNum)
                .usable(tProductInv.getUsable()-txNum)
                .build();
        tProductInvMapper.updateById(updateInv);

        vo.setOrderNo(String.valueOf(tOrders.getId()));
        return vo;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public FundOrderVO orderFund(FundOrderVO vo) {
        Date now = new Date();
        vo.setOrderDate(now);
        Long customerId = UserIdContext.getUserId();
        if(customerId==null){
            log.warn("当前客户不存在");
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }
        OperateTypeEnum operateTypeEnum = OperateTypeEnum.byCode(vo.getOperateType());
        if(operateTypeEnum==null){
            log.warn("操作类型错误");
            throw new BaseException(ErrorCode.ILLEGAL_OPERATE_TYPE);
        }
        TWallets myWallet = tWalletMapper.selectById(customerId);
        if(myWallet==null){
            log.warn("客户[{}]钱包未找到", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_WALLET);
        }
        // 基金是USDO进出
        TCustomers me = tCustomerMapper.selectById(customerId);
        if(me==null){
            log.warn("Id为[{}]的客户不存在", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER);
        }
        if(StateEnum.isStop(me.getState())){
            log.warn("Id为[{}]的客户已被封号", customerId);
            throw new BaseException(ErrorCode.STOPPED_CUSTOMER);
        }
        BigDecimal oriAmt = vo.getAmount();
        BigDecimal txAmt = vo.getAmount();
        BigDecimal beforeBalance = myWallet.getUsdo();
        BigDecimal beforeFund = myWallet.getFund();
        BigDecimal afterBalance = null;
        BigDecimal afterFund = null;
        BigDecimal charge = BigDecimal.ZERO;
        String direction = null;
        String remark = "基金投入支付";
        String journalType = JournalTypeEnum.FUND_ORDER.getCode();
        if(operateTypeEnum.getCode().equalsIgnoreCase(OperateTypeEnum.MANUAL.getCode())) {
            afterBalance = beforeBalance.subtract(txAmt);
            if(afterBalance.compareTo(BigDecimal.ZERO)<0){
                log.warn("客户[{}]的USDO余额不足,无法投入", customerId);
                throw new BaseException(ErrorCode.INSUFFICIENT_BALANCE);
            }
            direction = DirectionEnum.CREDIT.getCode();
            afterFund = beforeFund.add(txAmt);
        }
        if(operateTypeEnum.getCode().equalsIgnoreCase(OperateTypeEnum.WITHDRAW.getCode())) {
            // 基金提取手续费
            BigDecimal fundWithdrawRate = globalConfig.getFundWithdrawFee();
            charge = oriAmt.multiply(fundWithdrawRate).setScale(CommonConstants.DEFAULT_SCALE, RoundingMode.HALF_UP);
            txAmt = oriAmt.subtract(charge);
            afterFund = beforeFund.subtract(oriAmt);
            if(afterFund.compareTo(BigDecimal.ZERO)<0){
                log.warn("客户[{}]的基金余额[{}]不足,无法提出[{}](包含手续费[{}])", customerId, beforeFund, oriAmt, charge);
                throw new BaseException(ErrorCode.INSUFFICIENT_TX_AMOUNT);
            }
            remark = "基金提取收入";
            journalType = JournalTypeEnum.FUND_WITHDRAW.getCode();
            afterBalance = beforeBalance.add(txAmt);
            direction = DirectionEnum.DEBIT.getCode();
        }

        TOrders tOrders = TOrders.builder()
                .customerId(customerId)
                .orderType(OrderTypeEnum.FUNDS.getCode())
                .amount(oriAmt)
                .orderTime(now)
                .paymentTime(now)
                .paymentState(PaymentStateEnum.FINISHED.getCode())
                .state(StateEnum.NORMAL.getCode())
                .build();
        tOrderMapper.insert(tOrders);

        TFundOrderItems tFundOrderItems = TFundOrderItems.builder()
                .orderId(tOrders.getId())
                .operateType(operateTypeEnum.getCode())
                .build();
        tFundOrderItemMapper.insert(tFundOrderItems);

        // 基金产生的收益一定都是USDO
        TWallets updateWallet = TWallets.builder()
                .customerId(customerId)
                .usdo(afterBalance)
                .fund(afterFund)
                .build();
        tWalletMapper.updateById(updateWallet);

        TWalletJournal journal = TWalletJournal.builder()
                .customerId(customerId)
                .orderId(tOrders.getId())
                .journalType(journalType)
                .beforeBalance(beforeBalance)
                .amount(txAmt)
                .afterBalance(afterBalance)
                .charges(charge)
                .currency(CurrencyEnum.USDO.getCode())
                .direction(direction)
                .journalState(JournalStateEnum.FINISHED.getCode())
                .remark(remark)
                .build();

        tWalletJournalMapper.insert(journal);

        vo.setOrderNo(String.valueOf(tOrders.getId()));
        return vo;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ConversionChargeVO conversionCharge(ConversionChargeVO vo) {
        String srcCurrency = vo.getSrcCurrency();
        String dstCurrency = vo.getDstCurrency();
        CurrencyEnum srcCurrencyEnum = CurrencyEnum.byCode(srcCurrency);
        CurrencyEnum dstCurrencyEnum = CurrencyEnum.byCode(dstCurrency);
        if(srcCurrencyEnum==null || dstCurrencyEnum==null){
            log.warn("币种错误,不支持[{}]或[{}]", srcCurrency, dstCurrency);
            throw new BaseException(ErrorCode.ILLEGAL_CURRENCY);
        }
        BigDecimal conversionNum = vo.getConversionNum();
        if(conversionNum==null || conversionNum.compareTo(BigDecimal.ZERO)<=0){
            throw new BaseException(ErrorCode.TX_AMOUNT_ERROR);
        }
        // USDT转USDO无手续费
        if(CurrencyEnum.isUSDO(dstCurrency) && CurrencyEnum.isUSDT(srcCurrency)){
            vo.setAmount(conversionNum);
            vo.setCharge(BigDecimal.ZERO);
            return vo;
        }
        if(CurrencyEnum.isUSDO(srcCurrency) && CurrencyEnum.isUSDT(dstCurrency)){
            BigDecimal conversionRate = globalConfig.getBalanceConversionFee();
            BigDecimal charge = conversionNum.multiply(conversionRate).setScale(CommonConstants.DEFAULT_SCALE, RoundingMode.HALF_UP);
            vo.setAmount(conversionNum.subtract(charge));
            vo.setCharge(charge);
        }
        return vo;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public RechargeOrderVO orderRecharge(RechargeOrderVO vo) {
        Date now = new Date();
        CryptoCurrencyEnum cryptoCurrencyEnum = CryptoCurrencyEnum.byCode(vo.getCryptoCurrency());
        if(cryptoCurrencyEnum==null){
            throw new BaseException(ErrorCode.ILLEGAL_CRYPTO_CURRENCY);
        }
        String fromAddr = vo.getFrom();
        if(StringUtils.isBlank(fromAddr)){
            throw new BaseException(ErrorCode.REQUIRED_ACCOUNT_ADDRESS);
        }
        String toAddr = vo.getTo();
        if(StringUtils.isBlank(toAddr)){
            throw new BaseException(ErrorCode.REQUIRED_ACCOUNT_ADDRESS);
        }
        LambdaQueryWrapper<TWallets> wallQuer = new LambdaQueryWrapper<>();
        wallQuer.eq(TWallets::getAddr, toAddr);
        List<TWallets> walletsList = tWalletMapper.selectList(wallQuer);
        if(CollectionUtils.isEmpty(walletsList)){
            log.warn("收款地址[{}]未注册用户", toAddr);
            throw new BaseException(ErrorCode.NOT_EXIST_ADDRESS_CUSTOMER);
        }
        Map<Long, TWallets> walletMap = walletsList.stream().collect(Collectors.toMap(TWallets::getCustomerId, Function.identity()));
        List<Long> customerIdList = walletsList.stream().map(TWallets::getCustomerId).toList();

        LambdaQueryWrapper<TCustomers> custQuery = new LambdaQueryWrapper<>();
        custQuery.in(TCustomers::getId, customerIdList);
        custQuery.eq(TCustomers::getState, StateEnum.NORMAL.getCode());
        List<TCustomers> custList = tCustomerMapper.selectList(custQuery);
        if(CollectionUtils.isEmpty(custList)){
            log.warn("收款地址[{}]未注册用户", toAddr);
            throw new BaseException(ErrorCode.NOT_EXIST_ADDRESS_CUSTOMER);
        }
        TCustomers me = custList.get(0);
        Long customerId = me.getId();
        TWallets myWallet = walletMap.get(customerId);
        if(myWallet==null){
            log.warn("客户[{}]钱包未找到", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_WALLET);
        }

        String nowStr = DateUtils.formatDate(now, CommonConstants.DEFAULT_DATETIME_FORMAT);
        String orderDateStr = vo.getOrderDate();
        Date orderDate = DateUtils.parseDate(orderDateStr);

        BigDecimal expand = vo.getAmount();
        if(expand==null){
            throw new BaseException(ErrorCode.REQUIRED_TX_AMOUNT);
        }
        BigDecimal amount = expand.divide(new BigDecimal(CommonConstants.ETHER2WEI_STRING), RoundingMode.HALF_UP);
        TOrders tOrders = TOrders.builder()
                .customerId(customerId)
                .orderType(OrderTypeEnum.RECHARGE.getCode())
                .amount(amount)
                .orderTime(orderDate)
                .paymentTime(now)
                .paymentState(PaymentStateEnum.FINISHED.getCode())
                .state(StateEnum.NORMAL.getCode())
                .build();
        tOrderMapper.insert(tOrders);

        TRechargeOrderItems tRechargeOrderItems = TRechargeOrderItems.builder()
                .orderId(tOrders.getId())
                .cryptoCurrency(cryptoCurrencyEnum.getCode())
                .payAddr(fromAddr)
                .beneAddr(toAddr)
                .build();
        tRechargeOrderItemMapper.insert(tRechargeOrderItems);

        BigDecimal beforeBalance = myWallet.getUsdt();
        // 更新钱包余额
        BigDecimal afterBalance = beforeBalance.add(amount);

        TWalletJournal journal = TWalletJournal.builder()
                .customerId(customerId)
                .orderId(tOrders.getId())
                .journalType(JournalTypeEnum.RECHARGE.getCode())
                .beforeBalance(beforeBalance)
                .amount(amount)
                .afterBalance(afterBalance)
                .currency(CurrencyEnum.USDT.getCode())
                .direction(DirectionEnum.DEBIT.getCode())
                .journalState(JournalStateEnum.FINISHED.getCode())
                .remark("支付充值订单")
                .build();

        tWalletJournalMapper.insert(journal);

        TWallets updateWallet = TWallets.builder()
                .customerId(customerId)
                .usdt(afterBalance)
                .build();
        tWalletMapper.updateById(updateWallet);

        TCollectionJournal collectionJournal = TCollectionJournal.builder()
                .customerId(customerId)
                .orderId(tOrders.getId())
                .cryptoCurrency(cryptoCurrencyEnum.getCode())
                .amount(amount)
                .payAddr(toAddr)
                .beneAddr(globalConfig.getUsdtCollectionAddr())
                .journalState(JournalStateEnum.PENDING.getCode())
                .remark("充值归集记录")
                .build();
        tCollectionJournalMapper.insert(collectionJournal);

        //todo 与区块链交互, 完成归集(同步)
        String txHash = "";
        CollectVO collectVO = new CollectVO();
        collectVO.setCryptoCurrency(cryptoCurrencyEnum.getCode());
        collectVO.setFromAddress(myWallet.getAddr());
        collectVO.setGasPr(globalConfig.getChargePrivateKey());
        collectVO.setToAddress(globalConfig.getUsdtCollectionAddr());
        // 单位是WEI
        collectVO.setAmount(expand);
        ResponseEntity<Result> collectResp = restTemplate.postForEntity(UrlConstants.API_COLLECT_URL, collectVO, Result.class);
        if (collectResp.getStatusCode() == HttpStatus.OK) {
            Result result = collectResp.getBody();
            if(result!=null){
                Map data = (Map)result.getData();
                txHash = (String)data.get("hash");
            }else{
                log.warn("客户[{}]归集失败, 获取hash为空. {}", customerId, collectResp.getStatusCodeValue());
                throw new BaseException(ErrorCode.TX_FAILED);
            }
        } else {
            log.warn("客户[{}]归集失败. {}", customerId, collectResp.getStatusCodeValue());
            throw new BaseException(ErrorCode.TX_FAILED);
        }

        TCollectionJournal updateCollection = TCollectionJournal.builder()
                .id(collectionJournal.getId())
                .txHash(txHash)
                .journalState(JournalStateEnum.FINISHED.getCode())
                .build();
        tCollectionJournalMapper.updateById(updateCollection);

        vo.setOrderNo(String.valueOf(tOrders.getId()));
        vo.setPaymentTime(nowStr);
        return vo;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public SubscribeOrderVO orderSubscribe(SubscribeOrderVO vo) {
        Date now = new Date();
        vo.setOrderDate(now);
        Long customerId = UserIdContext.getUserId();
        if(customerId==null){
            log.warn("当前客户不存在");
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }
        SubscribeNameEnum subscribeNameEnum = SubscribeNameEnum.byCode(vo.getSubscribeName());
        if(subscribeNameEnum==null){
            log.warn("客户[{}]的认购包名称[{}]错误", customerId, vo.getSubscribeName());
            throw new BaseException(ErrorCode.ILLEGAL_SUBSCRIBE_NAME);
        }
        // 无论是USDT还是USDO购入, 产出都是USDO
        CurrencyEnum currencyEnum = CurrencyEnum.byCode(vo.getCurrency());
        if(currencyEnum==null){
            log.warn("客户[{}]的币种[{}]错误", customerId, vo.getCurrency());
            throw new BaseException(ErrorCode.ILLEGAL_CURRENCY);
        }
        TCustomers me = tCustomerMapper.selectById(customerId);
        if(me==null){
            log.warn("客户[{}]信息未找到", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER);
        }
        if(StateEnum.isStop(me.getState())){
            log.warn("Id为[{}]的客户已被封号", customerId);
            throw new BaseException(ErrorCode.STOPPED_CUSTOMER);
        }
        LambdaQueryWrapper<TSubscribe> subscribeQuery = new LambdaQueryWrapper<>();
        subscribeQuery.eq(TSubscribe::getSubscribeName, subscribeNameEnum.getCode());
        TSubscribe tSubscribe = tSubscribeMapper.selectOne(subscribeQuery);
        if(tSubscribe==null){
            log.warn("客户[{}]的认购包未找到", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER_SUBSCRIBE);
        }
        if(tSubscribe.getThreshold().compareTo(vo.getAmount())>0){
            log.warn("客户[{}]认购金额不足", customerId);
            throw new BaseException(ErrorCode.INSUFFICIENT_TX_AMOUNT);
        }

        TOrders tOrders = TOrders.builder()
                .customerId(customerId)
                .orderType(OrderTypeEnum.SUBSCRIBE.getCode())
                .amount(vo.getAmount())
                .orderTime(now)
                .paymentState(PaymentStateEnum.UNPAID.getCode())
                .state(StateEnum.NORMAL.getCode())
                .build();
        tOrderMapper.insert(tOrders);

        int subscribeDays = tSubscribe.getSubscribeDays();
        String subscribeLevel = tSubscribe.getSubscribeLevel();
        Date closeDate = DateUtils.getAddDate(now, Calendar.DAY_OF_YEAR, subscribeDays);

        TSubscribeOrderItems subscribeOrderItems = TSubscribeOrderItems.builder()
                .orderId(tOrders.getId())
                .subscribeName(subscribeNameEnum.getCode())
                .teamHandleState(HandleStateEnum.UNHANDLED.getCode())
                .returnHandleState(HandleStateEnum.UNHANDLED.getCode())
                .currency(currencyEnum.getCode())
                .closeTime(closeDate)
                .build();
        // 仅特殊认购包才需要处理直推奖励,普通认购包是没有直推奖励的,只有收益会有奖励
        if(SubscribeLevelEnum.isNormal(subscribeLevel)){
            subscribeOrderItems.setAwardHandleState(HandleStateEnum.HANDLED.getCode());
        }else{
            subscribeOrderItems.setAwardHandleState(HandleStateEnum.UNHANDLED.getCode());
        }
        tSubscribeOrderItemMapper.insert(subscribeOrderItems);

        // 计算预估收益
        BigDecimal estimatedReturn = vo.getAmount().add(vo.getAmount()
                .multiply(tSubscribe.getDailyRateReturn())
                .multiply(new BigDecimal(subscribeDays))
                .setScale(CommonConstants.DEFAULT_SCALE, RoundingMode.HALF_UP));

        vo.setOrderNo(tOrders.getId().toString());
        vo.setReturnDays(subscribeDays);
        vo.setPaymentState(PaymentStateEnum.UNPAID.getCode());
        vo.setEstimatedReturn(estimatedReturn);
        return vo;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public CancelOrderVO cancelOrder(CancelOrderVO vo) {
        Long customerId = UserIdContext.getUserId();
        if(customerId==null){
            log.warn("当前客户不存在");
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }
        TCustomers me = tCustomerMapper.selectById(customerId);
        if(me==null){
            log.warn("Id为[{}]的客户不存在", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER);
        }
        if(StateEnum.isStop(me.getState())){
            log.warn("Id为[{}]的客户已被封号", customerId);
            throw new BaseException(ErrorCode.STOPPED_CUSTOMER);
        }

        Long orderId = Long.valueOf(vo.getOrderNo());
        TOrders order = tOrderMapper.selectById(orderId);
        vo.setResult(false);
        if(order!=null) {
            String orderType = order.getOrderType();
            String paymentState = order.getPaymentState();
            if(PaymentStateEnum.FINISHED.getCode().equalsIgnoreCase(paymentState)
                    || PaymentStateEnum.DISABLED.getCode().equalsIgnoreCase(paymentState)){
                throw new BaseException(ErrorCode.FINISHED_CANNOT_CANCEL);
            }
            // 商品订单和认购包订单才能取消
            if(OrderTypeEnum.GOODS.getCode().equalsIgnoreCase(orderType) || OrderTypeEnum.SUBSCRIBE.getCode().equalsIgnoreCase(orderType)){
                Date now = new Date();
                TOrders updateOrder = TOrders.builder()
                        .id(orderId)
                        .cancelTime(now)
                        .paymentState(PaymentStateEnum.CANCEL.getCode())
                        .build();
                tOrderMapper.updateById(updateOrder);

                if(OrderTypeEnum.SUBSCRIBE.getCode().equalsIgnoreCase(orderType)){
                    LambdaQueryWrapper<TSubscribeOrderItems> queryItem = new LambdaQueryWrapper<>();
                    queryItem.eq(TSubscribeOrderItems::getOrderId, orderId);
                    TSubscribeOrderItems orderItem = tSubscribeOrderItemMapper.selectOne(queryItem);
                    if(orderItem!=null){
                        String returnHandleState = orderItem.getReturnHandleState();
                        if(!HandleStateEnum.UNHANDLED.getCode().equalsIgnoreCase(returnHandleState)){
                            throw new BaseException(ErrorCode.HANDLING_CANNOT_CANCEL);
                        }
                        TSubscribeOrderItems updateItem = TSubscribeOrderItems.builder()
                                .id(orderItem.getId())
                                .returnHandleState(HandleStateEnum.CANCEL.getCode())
                                .build();
                        tSubscribeOrderItemMapper.updateById(updateItem);
                    }
                }
                vo.setResult(true);
            }
        }
        return vo;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public WithdrawOrderVO orderWithdraw(WithdrawOrderVO vo) {
        Date now = new Date();
        vo.setOrderDate(now);
        Long customerId = UserIdContext.getUserId();
        if(customerId==null){
            log.warn("当前客户不存在");
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }
        String cryptoCurrency = vo.getCryptoCurrency();
        CryptoCurrencyEnum cryptoCurrencyEnum = CryptoCurrencyEnum.byCode(cryptoCurrency);
        if(cryptoCurrencyEnum==null){
            log.warn("提现币种错误,不支持[{}]", cryptoCurrency);
            throw new BaseException(ErrorCode.ILLEGAL_CRYPTO_CURRENCY);
        }

        TCustomers me = tCustomerMapper.selectById(customerId);
        if(me==null){
            log.warn("Id为[{}]的客户不存在", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER);
        }
        if(StateEnum.isStop(me.getState())){
            log.warn("Id为[{}]的客户已被封号", customerId);
            throw new BaseException(ErrorCode.STOPPED_CUSTOMER);
        }
        TWallets myWallet = tWalletMapper.selectById(customerId);
        if(myWallet==null) {
            log.warn("客户[{}]钱包未找到", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_WALLET);
        }
        if(StringUtils.isBlank(me.getTxPassword())){
            log.warn("客户[{}]未设置交易密码", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_TX_PASSWORD);
        }
        String withdrawAddress = vo.getAddress();
        if(StringUtils.isBlank(withdrawAddress)){
            log.warn("客户[{}]未设置提现地址", customerId);
            throw new BaseException(ErrorCode.REQUIRED_ADDRESS);
        }
        if(withdrawAddress.length()!=42 || !(withdrawAddress.startsWith("0x") || withdrawAddress.startsWith("0X"))){
            log.warn("客户[{}]提现地址[{}]不合法", customerId, withdrawAddress);
            throw new BaseException(ErrorCode.ADDRESS_FORMAT_ERROR);
        }
        BigDecimal withdarwAmt = vo.getWithdrawNum();
        BigDecimal inputAmount = vo.getAmount();
        BigDecimal inputCharge = vo.getCharge();
        BigDecimal highest = vo.getWithdrawHighest();
        BigDecimal lowest = vo.getWithdrawLowest();
        if(withdarwAmt.compareTo(lowest)<0 || withdarwAmt.compareTo(highest)>0){
            log.warn("客户[{}]提现金额超过限制", customerId);
            throw new BaseException(ErrorCode.TX_AMOUNT_ERROR);
        }

        WithdrawChargeVO chargeVO = new WithdrawChargeVO();
        BeanUtils.copyProperties(vo, chargeVO);
        WithdrawChargeVO retVO = withdrawCharge(chargeVO);
        BigDecimal amount = retVO.getAmount();
        BigDecimal charge = retVO.getCharge();
        if(inputAmount.compareTo(amount)!=0){
            log.warn("客户[{}]实际到账金额与计算结果不一致", customerId);
            throw new BaseException(ErrorCode.TX_AMOUNT_ERROR);
        }
        if(inputCharge.compareTo(charge)!=0){
            log.warn("客户[{}]手续费与计算结果不一致", customerId);
            throw new BaseException(ErrorCode.TX_AMOUNT_ERROR);
        }
        BigDecimal beforeBalance = myWallet.getUsdt();
        BigDecimal afterBalance = beforeBalance.subtract(amount).subtract(charge);
        if(afterBalance.compareTo(BigDecimal.ZERO)<0){
            log.warn("客户[{}]的usdt余额不足,无法提取", customerId);
            throw new BaseException(ErrorCode.INSUFFICIENT_BALANCE);
        }
        String txPassword = vo.getTxPassword();
        if(StringUtils.isBlank(txPassword)){
            throw new BaseException(ErrorCode.REQUIRED_TX_PASSWORD);
        }
        if(!argon2Encoder.matches(txPassword, me.getTxPassword())){
            throw new BaseException(ErrorCode.TX_PASSWORD_ERROR);
        }
        TOrders tOrders = TOrders.builder()
                .customerId(customerId)
                .orderType(OrderTypeEnum.WITHDRAW.getCode())
                .amount(withdarwAmt)
                .orderTime(now)
                .paymentState(PaymentStateEnum.UNPAID.getCode())
                .state(StateEnum.NORMAL.getCode())
                .build();
        tOrderMapper.insert(tOrders);

        TWithdrawOrderItems tWithdrawOrderItems = TWithdrawOrderItems.builder()
                .orderId(tOrders.getId())
                .cryptoCurrency(cryptoCurrency)
                .amount(amount)
                .charges(charge)
                .handleState(HandleStateEnum.UNHANDLED.getCode())
                .beneAddr(withdrawAddress)
                .build();
        tWithdrawOrderItemMapper.insert(tWithdrawOrderItems);

        // 更新钱包余额
        TWallets updateWallet = TWallets.builder()
                .customerId(customerId)
                .usdt(afterBalance)
                .build();
        tWalletMapper.updateById(updateWallet);

        // 产生待处理流水
        TWalletJournal journal = TWalletJournal.builder()
                .customerId(customerId)
                .orderId(tOrders.getId())
                .journalType(JournalTypeEnum.WITHDRAW.getCode())
                .beforeBalance(beforeBalance)
                .amount(amount)
                .afterBalance(afterBalance)
                .charges(charge)
                .currency(CurrencyEnum.USDT.getCode())
                .direction(DirectionEnum.CREDIT.getCode())
                .journalState(JournalStateEnum.PENDING.getCode())
                .remark("支付提现订单")
                .build();
        tWalletJournalMapper.insert(journal);

        vo.setOrderNo(String.valueOf(tOrders.getId()));
        return vo;
    }

    @Override
    public WithdrawChargeVO withdrawCharge(WithdrawChargeVO vo) {
        String cryptoCurrency = vo.getCryptoCurrency();
        CryptoCurrencyEnum cryptoCurrencyEnum = CryptoCurrencyEnum.byCode(cryptoCurrency);
        if(cryptoCurrencyEnum==null){
            log.warn("提现币种错误, 不支持[{}]", cryptoCurrency);
            throw new BaseException(ErrorCode.ILLEGAL_CRYPTO_CURRENCY);
        }
        Long customerId = UserIdContext.getUserId();
        if(customerId==null){
            log.warn("当前客户不存在");
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }
        BigDecimal withdrawNum = vo.getWithdrawNum();
        if(withdrawNum==null || withdrawNum.compareTo(BigDecimal.ZERO)<=0){
            log.warn("客户[{}]提现币种数量应大于0", customerId);
            throw new BaseException(ErrorCode.TX_AMOUNT_ERROR);
        }
        BigDecimal withdrawRate = globalConfig.getWithdrawFee();
        BigDecimal charge = withdrawNum.multiply(withdrawRate).setScale(CommonConstants.DEFAULT_SCALE, RoundingMode.HALF_UP);
        BigDecimal amt = withdrawNum.subtract(charge);

        vo.setAmount(amt);
        vo.setCharge(charge);
        return vo;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public ConversionOrderVO orderConversion(ConversionOrderVO vo) {
        Date now = new Date();
        vo.setOrderDate(now);
        Long customerId = UserIdContext.getUserId();
        if(customerId==null){
            log.warn("当前客户不存在");
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }
        TCustomers me = tCustomerMapper.selectById(customerId);
        if(me==null){
            log.warn("Id为[{}]的客户不存在", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER);
        }
        if(StateEnum.isStop(me.getState())){
            log.warn("Id为[{}]的客户已被封号", customerId);
            throw new BaseException(ErrorCode.STOPPED_CUSTOMER);
        }
        TWallets myWallet = tWalletMapper.selectById(customerId);
        if(myWallet==null) {
            log.warn("客户[{}]钱包未找到", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_WALLET);
        }
        String txPassword = vo.getTxPassword();
        if(StringUtils.isBlank(txPassword)){
            throw new BaseException(ErrorCode.REQUIRED_TX_PASSWORD);
        }
        if(!argon2Encoder.matches(txPassword, me.getTxPassword())){
            throw new BaseException(ErrorCode.TX_PASSWORD_ERROR);
        }

        ConversionChargeVO chargeVO = new ConversionChargeVO();
        BeanUtils.copyProperties(vo, chargeVO);
        ConversionChargeVO chargeRetVO = conversionCharge(chargeVO);
        BigDecimal amount = chargeRetVO.getAmount();
        BigDecimal charge = chargeRetVO.getCharge();
        BigDecimal conversionNum = vo.getConversionNum();

        if(vo.getAmount().compareTo(amount)!=0){
            log.warn("客户[{}]实际到账金额与计算结果不一致", customerId);
            throw new BaseException(ErrorCode.TX_AMOUNT_ERROR);
        }
        if(vo.getCharge().compareTo(charge)!=0){
            log.warn("客户[{}]手续费与计算结果不一致", customerId);
            throw new BaseException(ErrorCode.TX_AMOUNT_ERROR);
        }
        BigDecimal beforeUsdo = myWallet.getUsdo();
        BigDecimal beforeUsdt = myWallet.getUsdt();
        BigDecimal afterUsdo = BigDecimal.ZERO;
        BigDecimal afterUsdt = BigDecimal.ZERO;
        BigDecimal chargeUsdo = BigDecimal.ZERO;
        BigDecimal chargeUsdt = BigDecimal.ZERO;
        String directionUsdo = null;
        String directionUsdt = null;
        String remark = "";
        // USDO->USDT 需要手续费
        if(CurrencyEnum.isUSDO(vo.getSrcCurrency()) && CurrencyEnum.isUSDT(vo.getDstCurrency())){
            afterUsdo = beforeUsdo.subtract(conversionNum);
            if(afterUsdo.compareTo(BigDecimal.ZERO)<0){
                log.warn("客户[{}]的USDO余额不足", customerId);
                throw new BaseException(ErrorCode.INSUFFICIENT_BALANCE);
            }
            afterUsdt = beforeUsdt.add(amount);
            directionUsdo = DirectionEnum.CREDIT.getCode();
            directionUsdt = DirectionEnum.DEBIT.getCode();
            chargeUsdt = charge;
            remark = "USDO->USDT";
        }
        // USDT->USDO 不需要手续费
        if(CurrencyEnum.isUSDT(vo.getSrcCurrency()) && CurrencyEnum.isUSDO(vo.getDstCurrency())){
            afterUsdt = beforeUsdt.subtract(conversionNum);
            if(afterUsdt.compareTo(BigDecimal.ZERO)<0){
                log.warn("客户[{}]的USDT余额不足", customerId);
                throw new BaseException(ErrorCode.INSUFFICIENT_BALANCE);
            }
            afterUsdo = beforeUsdo.add(conversionNum);
            directionUsdt = DirectionEnum.CREDIT.getCode();
            directionUsdo = DirectionEnum.DEBIT.getCode();
            remark = "USDT->USDO";
        }

        TOrders tOrder = TOrders.builder()
                .customerId(customerId)
                .amount(vo.getConversionNum())
                .orderTime(now)
                .orderType(OrderTypeEnum.CONVERSION.getCode())
                .paymentState(PaymentStateEnum.FINISHED.getCode())
                .paymentTime(now)
                .state(StateEnum.NORMAL.getCode())
                .build();
        tOrderMapper.insert(tOrder);

        TWallets updateWallet = TWallets.builder()
                .customerId(customerId)
                .usdo(afterUsdo)
                .usdt(afterUsdt)
                .build();
        tWalletMapper.updateById(updateWallet);

        TWalletJournal usdoJournal = TWalletJournal.builder()
                .customerId(customerId)
                .orderId(tOrder.getId())
                .journalType(JournalTypeEnum.BAL_CONVERSION.getCode())
                .beforeBalance(beforeUsdo)
                .amount(amount)
                .afterBalance(afterUsdo)
                .charges(chargeUsdo)
                .currency(CurrencyEnum.USDO.getCode())
                .direction(directionUsdo)
                .journalState(JournalStateEnum.FINISHED.getCode())
                .remark(remark)
                .build();

        TWalletJournal usdtJournal = TWalletJournal.builder()
                .customerId(customerId)
                .orderId(tOrder.getId())
                .journalType(JournalTypeEnum.BAL_CONVERSION.getCode())
                .beforeBalance(beforeUsdt)
                .amount(amount)
                .afterBalance(afterUsdt)
                .charges(chargeUsdt)
                .currency(CurrencyEnum.USDT.getCode())
                .direction(directionUsdt)
                .journalState(JournalStateEnum.FINISHED.getCode())
                .remark(remark)
                .build();

        tWalletJournalMapper.insert(usdoJournal);
        tWalletJournalMapper.insert(usdtJournal);
        return vo;
    }

    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    @Override
    public DeleteOrderVO deleteOrder(DeleteOrderVO vo) {
        Long customerId = UserIdContext.getUserId();
        if(customerId==null){
            log.warn("当前客户不存在");
            throw new BaseException(ErrorCode.UNAUTHORIZED);
        }
        TCustomers me = tCustomerMapper.selectById(customerId);
        if(me==null){
            log.warn("Id为[{}]的客户不存在", customerId);
            throw new BaseException(ErrorCode.NOT_EXIST_CUSTOMER);
        }
        if(StateEnum.isStop(me.getState())){
            log.warn("Id为[{}]的客户已被封号", customerId);
            throw new BaseException(ErrorCode.STOPPED_CUSTOMER);
        }

        Long orderId = Long.valueOf(vo.getOrderNo());
        vo.setResult(false);

        TOrders order = tOrderMapper.selectById(orderId);
        if(order!=null){
            String orderType = order.getOrderType();
            String paymentState = order.getPaymentState();
            if(OrderTypeEnum.GOODS.getCode().equalsIgnoreCase(orderType)) {
                LambdaQueryWrapper<TGoodsOrderItems> queryWrapper = new LambdaQueryWrapper<>();
                queryWrapper.eq(TGoodsOrderItems::getOrderId, orderId);
                TGoodsOrderItems item = tGoodsOrderItemMapper.selectOne(queryWrapper);
                if(item!=null){
                    // 已取消的商品订单才能删除
                    if(PaymentStateEnum.CANCEL.getCode().equalsIgnoreCase(paymentState)){
                        tOrderMapper.deleteById(orderId);
                        vo.setResult(true);
                    }else{
                        throw new BaseException(ErrorCode.ORDER_CANNOT_DELETE);
                    }
                }
            }
        }
        return vo;
    }
}
